# -*- coding:utf-8 -*-
# @File    : apiSend.py
# ***********************
import logging
import allure
import time,os
from config import INTERVAL
from comm.unit import apiMethod,authentication
# from comm.unit.authentication import authentication
from comm.unit.cacheData import *
import base64
import datetime
import hmac
import urllib.parse
from hashlib import sha256
from urllib.parse import unquote, urlparse, quote
import json
from comm.utils import readYaml
from config import *
from ast import literal_eval

aconfig = readYaml.read_yaml_data(API_CONFIG)
run_evn = RC["run_evn"]
relevance = aconfig[PROJECT_NAME][run_evn]

def cal_encrypt_sign(method: str, url: str, access_key: str, secret_key: str, dt_str=None):
    url = unquote(url)
    o = urlparse(url)
    url_encode = urllib.parse.quote
    # params
    pair_list = (
        list(
            [*pair.split("="), idx] if len(pair.split("=")) == 2 else [pair, None, idx]
            for idx, pair in enumerate(o.query.split("&"))
        )
        if o.query
        else ""
    )
    sorted_quote = []
    # key 按照字典顺序（ASCII 码由小到大）排序（另外，go中按key排序后同名按原次序排序， python为与中台服务保持一致，另加一个同名按次序排序
    sorted_pair_list = sorted(pair_list, key=lambda x: (x[0], x[2]))
    for item in sorted_pair_list:
        if item[1] == None:
            # a. 当该项只有 key 时，转换公式为 url_encode(key) + "=" 的形式；
            sorted_quote.append(url_encode(item[0]) + "=")
        else:
            # b. 当该项是 key=value 的形式时，转换公式为 url_encode(key) + "=" + url_encode(value) 的形式，value 可以是空字符串；
            sorted_quote.append(url_encode(item[0]) + "=" + url_encode(item[1]))

    canonical_query_string = "&".join(sorted_quote)
    # print("canonical_query_string", canonical_query_string)
    # fmt 时间
    date_fmt_str = (
        datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT")
        if not dt_str
        else dt_str
    )
    # date_fmt_str = "Thu, 29 Jul 2021 11:51:11 GMT"
    signing_string = (
        f"{method}\n{o.path}\n{canonical_query_string}\n{access_key}\n{date_fmt_str}\n"
    )
    # print(signing_string)

    sign = base64.b64encode(
        hmac.new(
            secret_key.encode("utf-8"), signing_string.encode("utf-8"), digestmod=sha256
        ).digest()
    )
    sign_string = str(sign, "utf-8")

    return {
        "Date": date_fmt_str,
        "X-Hmac-Access-Key": access_key,
        "X-Hmac-Algorithm": "hmac-sha256",
        "X-Hmac-Signature": sign_string,
    }


def send_request(test_info, case_data, case_yaml):
    """
    封装请求
    :param test_info: 测试信息
    :param case_data: 用例数据
    :return:
    """
    try:
        # 获取用例基本信息
        _host = test_info["host"].replace("${","").replace("}","")
        host = relevance[_host]
        _scheme = test_info["scheme"].replace("${", "").replace("}", "")
        scheme = relevance[_scheme]
        method = test_info["method"].upper()
        address = test_info["address"]
        mime_type = test_info["mime_type"]
        if "timesleep" in test_info.keys():
            timesleep = test_info["timesleep"]
            time.sleep(timesleep)

        if test_info["headers"] != False:
            _headers = test_info["headers"].replace("${", "").replace("}", "")
            headers = relevance[_headers]
        else:
            headers = test_info["headers"]
        if "def" in str(headers):
            _def = getattr(authentication, headers["def"], None)
            headers = _def()
        logging.debug("获取到的header:".format(headers))
        # print('test_info["cookies"]:',test_info["cookies"])
        if test_info["cookies"] != False:
            _cookies = test_info["cookies"].replace("${", "").replace("}", "")
            cookies = relevance[_cookies]
        elif "cookies" not in relevance:
            cookies = None
        else:
            if relevance["cookies"]:
                cookies = relevance["cookies"]
            else:
                cookies = test_info["cookies"]
        if "def" in str(cookies):
            _def = getattr(authentication, cookies["def"], None)
            cookies = _def()
        file = test_info["file"]
        if file == False:
            pass
        else:
            curPath = os.path.abspath(os.path.dirname(__file__))
            if isinstance(file, dict):
                for key in file:
                    filePath = curPath.split('comm')[0] + '/file/' + file[key]
                    if os.path.isfile(filePath):
                        file = {key: open(filePath, 'rb')}
                    elif os.path.isfile(file[key]):
                        file = {key: open(file[key], 'rb')}
                    else:
                        raise KeyError('找不到对应的文件路径：{}'.format(test_info["file"]))
            else:
                filePath = curPath.split('comm')[0]+'/file/' + file
                if os.path.isfile(filePath):
                    file = {'file': open(filePath, 'rb')}
                elif os.path.isfile(file):
                    file = {'file': open(file, 'rb')}
                else:
                    raise KeyError('找不到对应的文件路径：{}'.format(test_info["file"]))
        timeout = relevance["timeout"]
        summary = case_data["summary"]
        parameter = case_data["parameter"]
        if case_data["parameter"] == '{}':
            parameter = None
    except Exception as e:
        raise KeyError('获取用例基本信息失败：{}'.format(e))
    request_url = str(scheme) + "://" + str(host) + str(address)
    logging.info("=" * 150)
    logging.info("请求接口：%s" % str(summary))
    logging.info("请求地址：%s" % request_url)
    logging.info("请求头: %s" % str(headers))
    logging.info("请求参数: %s" % str(parameter))

    # 判断是否保存cookies
    if summary == 'save_cookies':
        with allure.step("保存cookies信息"):
            allure.attach(name="请求接口", body=str(summary))
            allure.attach(name="请求地址", body=request_url)
            allure.attach(name="请求头", body=str(headers))
            allure.attach(name="请求参数", body=str(parameter))
            apiMethod.save_cookie(headers=headers,
                                  address=request_url,
                                  mime_type=mime_type,
                                  data=parameter,
                                  cookies=cookies,
                                  timeout=timeout)
    # 判断接口请求类型
    if method == 'POST':
        logging.info("请求方法: POST")
        # 判断是否上传文件
        if file:
            with allure.step("POST上传文件"):
                allure.attach(name="请求接口", body=str(summary))
                allure.attach(name="请求地址", body=request_url)
                allure.attach(name="请求头", body=str(headers))
                allure.attach(name="请求文件", body=str(file))
            result = apiMethod.post(headers=headers,
                                    address=request_url,
                                    mime_type='multipart/form-data',
                                    data=parameter,
                                    files=file,
                                    cookies=cookies,
                                    timeout=timeout)
        else:
            with allure.step("POST请求接口"):
                allure.attach(name="请求接口", body=str(summary))
                allure.attach(name="请求地址", body=request_url)
                allure.attach(name="请求头", body=str(headers))
                allure.attach(name="请求参数", body=str(parameter))
            result = apiMethod.post(headers=headers,
                                    address=request_url,
                                    mime_type=mime_type,
                                    data=parameter,
                                    cookies=cookies,
                                    timeout=timeout)
    elif method == 'GET':
        logging.info("请求方法: GET")
        with allure.step("GET请求接口"):
            allure.attach(name="请求接口", body=str(summary))
            allure.attach(name="请求地址", body=request_url)
            allure.attach(name="请求头", body=str(headers))
            allure.attach(name="请求参数", body=str(parameter))
        if parameter != None:
            param_list = []
            if not isinstance(parameter,dict):
                parameter = literal_eval(parameter)
            for key, value in parameter.items():
                param = key + '=' + str(value)
                param_list.append(param)
            params = "&".join(param_list)
            request_url = scheme+"://" + host + address +"?" + quote(params)
        result = apiMethod.get(headers=headers,
                               address=request_url,
                               data=parameter,
                               files=file,
                               cookies=cookies,
                               timeout=timeout)
        print("file::",file)
    elif method == 'PUT':
        logging.info("请求方法: PUT")
        # 判断是否上传文件
        if file:
            with allure.step("PUT上传文件"):
                allure.attach(name="请求接口", body=str(summary))
                allure.attach(name="请求地址", body=request_url)
                allure.attach(name="请求头", body=str(headers))
                allure.attach(name="请求参数", body=str(parameter))
            result = apiMethod.put(headers=headers,
                                   address=request_url,
                                   mime_type=mime_type,
                                   files=parameter,
                                   cookies=cookies,
                                   timeout=timeout)
        else:
            with allure.step("PUT请求接口"):
                allure.attach(name="请求接口", body=str(summary))
                allure.attach(name="请求地址", body=request_url)
                allure.attach(name="请求头", body=str(headers))
                allure.attach(name="请求参数", body=str(parameter))


            result = apiMethod.put(headers=headers,
                                   address=request_url,
                                   mime_type=mime_type,
                                   data=parameter,
                                   cookies=cookies,
                                   timeout=timeout)
    elif method == 'DELETE':
        logging.info("请求方法: DELETE")
        with allure.step("DELETE请求接口"):
            allure.attach(name="请求接口", body=str(summary))
            allure.attach(name="请求地址", body=request_url)
            allure.attach(name="请求头", body=str(headers))
            allure.attach(name="请求参数", body=str(parameter))
        result = apiMethod.delete(headers=headers,
                                  address=request_url,
                                  data=parameter,
                                  cookies=cookies,
                                  timeout=timeout)
    else:
        result = {"code": None, "data": None}
    # logging.info("请求接口结果：\n %s" % str(result))
    time.sleep(INTERVAL)
    #接口运行成功后将返回值按接口用例名：{}返回结果缓存起来
    try:
        cache.set(case_yaml,result[1])
        res = cache.get(case_yaml)
    except:
        cache.set(case_yaml,None)
    return result
